package com.uh.rds.testing.validator;

import com.uh.rds.testing.base.DataValidator;
import com.uh.rds.testing.base.DataValidatorStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

public class DataValidatorResult {

    private static Logger logger = LoggerFactory.getLogger(DataValidatorResult.class);

    private List<DataValidatorThread> threads;

    private int threadsSize;

    private List<DataValidatorStep> steps;

    private HashMap<String, ValidatorSummary> validatorSummaries = new LinkedHashMap<>(); // 每种validator类型的汇总（key为validator类名，value为ValidatorSummary对象）

    public DataValidatorResult(DataValidatorStep[] steps, List<DataValidatorThread> threads) {


        this.steps = Arrays.asList(steps);
        this.threads = threads;
        this.threadsSize = threads.size();

        for (DataValidatorThread thread : threads) {
            for(DataValidator validator : thread.getDataValidators()) {
                ValidatorSummary summary = validatorSummaries.get(validator.getClass().getName());
                if(summary == null) {
                    summary = new ValidatorSummary(validator.getClass());
                    validatorSummaries.put(validator.getClass().getName(), summary);
                }
                summary.addCount(1);
                summary.addSize(validator.getSize());
                summary.addProcSize(validator.getProcSize());
                summary.addValidSize(validator.getValidSize());
                summary.addClearSize(validator.getClearSize());
                summary.addProcTime(validator.getProcessTime());
                summary.addValidTime(validator.getValidateTime());
            }
        }

    }

    public String summary() {
        StringBuilder sb = new StringBuilder();
        sb.append("MultiThreadDataTester Result:\n");
        sb.append(" steps: " + steps + "\n");
        sb.append(" threads: " + threadsSize + "\n");
        sb.append(" validatorSummaries: \n");
        for(ValidatorSummary summary : validatorSummaries.values()) {
            sb.append("  " + summary.toString() + "\n");
        }

        return sb.toString();
    }



    public List<ValidatorSummary> validatorSummaryList() {
        List<ValidatorSummary> validatorSummaryList = new ArrayList<>();
        for(ValidatorSummary summary : validatorSummaries.values()) {
            validatorSummaryList.add(summary);
        }
        return validatorSummaryList;
    }

    public static void validatorSummaryList(List<ValidatorSummary> validatorSummaryList) {
        Map<Class<? extends DataValidator>, Integer> size1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingInt(ValidatorSummary::getSize)
                ));
        Map<Class<? extends DataValidator>, Integer> proc1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingInt(ValidatorSummary::getProcSize)
                ));
        Map<Class<? extends DataValidator>, Integer> valid1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingInt(ValidatorSummary::getValidSize)
                ));
        Map<Class<? extends DataValidator>, Integer> clear1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingInt(ValidatorSummary::getClearSize)
                ));
        Map<Class<? extends DataValidator>, Long> procTime1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingLong(ValidatorSummary::getAverageProcTime)
                ));
        Map<Class<? extends DataValidator>, Long> validTime1 = validatorSummaryList.stream()
                .collect(Collectors.groupingBy(
                        ValidatorSummary::getClazz,
                        Collectors.summingLong(ValidatorSummary::getAverageValidTime)
                ));
        System.out.println("\n");
        System.out.println("final Result:");
        for(Class<? extends DataValidator> c : size1.keySet()){
            System.out.println(c.getSimpleName() + "{size=" + size1.get(c) + ", proc=" + proc1.get(c)
                    + ", processTime=" + procTime1.get(c)
                    + ", valid=" + valid1.get(c)
                    + ", validateTime=" + validTime1.get(c)
                    + ", clear=" + clear1.get(c) + "}");
        }

    }


    /**
     * 是否通过测试, 并通过日输出错误validate日志
     * @return
     */
    public boolean isPassed() {
        boolean result = true;
        for (DataValidatorThread thread : threads) {
            for (DataValidator validator : thread.getDataValidators()) {
                boolean passed = true;
                List<DataValidatorStep> failedSteps = new ArrayList<>(2);
                if(steps.contains(DataValidatorStep.PROCESS)){
                    if(validator.getSize() != validator.getProcSize()) {
                        passed = false;
                        failedSteps.add(DataValidatorStep.PROCESS);
                    }
                }
                if(steps.contains(DataValidatorStep.VALIDATE)){
                    if(validator.getProcSize() != validator.getValidSize()) {
                        passed = false;
                        failedSteps.add(DataValidatorStep.VALIDATE);
                    }
                }
                if(steps.contains(DataValidatorStep.VALIDATE_EXPIRED)){
                    if(validator.getProcSize() != validator.getValidSize()) {
                        passed = false;
                        failedSteps.add(DataValidatorStep.VALIDATE_EXPIRED);
                    }
                }
                if(steps.contains(DataValidatorStep.CLEAR)){
                    if(validator.getProcSize() != validator.getClearSize()) {
                        passed = false;
                        failedSteps.add(DataValidatorStep.CLEAR);
                    }
                }

                // 如果有一个validator没有通过，设置result为false， 并记录日志
                if(passed == false) {
                    result = false;
                    logger.error(validator + " failed in " + failedSteps);
                }
            }
        }

        return result;
    }

    public static class ValidatorSummary {

        private Class<? extends DataValidator> clazz;

        private int count = 0; // 统计该类型的validator数量

        private int size = 0;  // 统计该类型的validator的总数据量

        private int procSize = 0; // 统计该类型的validator的处理数据量

        private int validSize = 0; // 统计该类型的validator的验证数据量

        private int clearSize = 0; // 统计该类型的validator的清除数据量

        private long procTime = 0; // 处理过程的总时长

        private long validTime = 0; // 验证过程的总时长

        public ValidatorSummary(Class<? extends DataValidator> clazz) {
            this.clazz = clazz;
        }

        public Class<? extends DataValidator> getClazz() {
            return clazz;
        }

        public void setClazz(Class<? extends DataValidator> clazz) {
            this.clazz = clazz;
        }

        public int getSize() {
            return size;
        }

        public void setSize(int size) {
            this.size = size;
        }

        public int getProcSize() {
            return procSize;
        }

        public void setProcSize(int procSize) {
            this.procSize = procSize;
        }

        public int getValidSize() {
            return validSize;
        }

        public void setValidSize(int validSize) {
            this.validSize = validSize;
        }

        public int getClearSize() {
            return clearSize;
        }

        public void setClearSize(int clearSize) {
            this.clearSize = clearSize;
        }

        public int getCount() {
            return count;
        }

        public void addCount(int count) {
            this.count += count;
        }

        public void addProcTime(long procTime) {
            this.procTime += procTime;
        }

        public void addValidTime(long validTime) {
            this.validTime += validTime;
        }

        public void addSize(int size) {
            this.size += size;
        }

        public void addProcSize(int procSize) {
            this.procSize += procSize;
        }

        public void addValidSize(int validSize) {
            this.validSize += validSize;
        }

        public void addClearSize(int clearSize) {
            this.clearSize += clearSize;
        }

        public long getAverageValidTime() {
            if(count == 0) {
                return 0;
            }
            return validTime / count;
        }

        public long getAverageProcTime() {
            if(count == 0) {
                return 0;
            }
            return procTime / count;
        }

        public String toString() {
            return clazz.getSimpleName() + "{Count=" + count + ", DataSize=" + size + ", DataProceed=" + procSize
                    + ", AverageProcessTime=" + getAverageProcTime() + ", DataValidated=" + validSize
                    + ", AverageValidateTime=" + getAverageValidTime() + ", DataCleared=" + clearSize + "}" ;
        }


    }


}
